#include "SolidNoise.h"
#include "../Basis/RNG.h"

SolidNoise::SolidNoise()
{
	RNG random;

	grad[0] = Vector3(1, 1, 0);
	grad[1] = Vector3(-1, 1, 0);
	grad[2] = Vector3(1, -1, 0);
	grad[3] = Vector3(-1, -1, 0);

	grad[4] = Vector3(1, 0, 1);
	grad[5] = Vector3(-1, 0, 1);
	grad[6] = Vector3(1, 0, -1);
	grad[7] = Vector3(-1, 0, -1);

	grad[8] = Vector3(0, 1, 1);
	grad[9] = Vector3(0, -1, 1);
	grad[10] = Vector3(0, 1, -1);
	grad[11] = Vector3(0, -1, -1);

	grad[12] = Vector3(1, 1, 0);
	grad[13] = Vector3(-1, 1, 0);
	grad[14] = Vector3(0, -1, 1);
	grad[15] = Vector3(0, -1, -1);

	for (int i = 0; i < PERLIN_VECTOR_NUM; i++)
	{
		phi[i] = i;
	}

	// generate a permutation for phi[i]
	int temp;
	int index;
	for (int i = PERLIN_VECTOR_NUM - 1; i >= 1; i--)
	{
		//	generate index from 0, 1, ..., i
		index = (int)(random() * (i + 1));
		//	swap phi[i] and phi[index]
		temp = phi[index];
		phi[index] = phi[i];
		phi[i] = temp;
	}
}

float SolidNoise::turbulence(const Vector3& p, int depth) const
{
	float sum = 0.0f;
	float weight = 1.0f;
	Vector3 ptemp(p);

	sum = fabs(noise(ptemp));

	for (int i = 0; i < depth; i++)
	{
		weight *= 2;
		ptemp.setX(p.x() * weight);
		ptemp.setY(p.y() * weight);
		ptemp.setZ(p.z() * weight);

		sum += fabs(noise(ptemp)) / weight;
	}
	return sum;
}

float SolidNoise::dturbulence(const Vector3& p, int depth, float d) const
{
	float sum = 0.0f;
	float weight = 1.0f;
	Vector3 ptemp(p);

	sum = fabs(noise(ptemp)) / d;

	for (int i = 0; i < depth; i++)
	{
		weight *= d;
		ptemp.setX(p.x() * weight);
		ptemp.setY(p.y() * weight);
		ptemp.setZ(p.z() * weight);

		sum += fabs(noise(ptemp)) / d;
	}
	return sum;
}

float SolidNoise::noise(const Vector3& p) const
{
	int fi, fj, fk;
	fi = (int)floor(p.x());
	fj = (int)floor(p.y());
	fk = (int)floor(p.z());

	float sum = 0.0f;
	Vector3 v;
	for (int i = fi; i <= fi + 1; i++)
	{
		v.setX(p.x() - (float)i);
		for (int j = fj; j <= fj + 1; j++)
		{
			v.setY(p.y() - (float)j);
			for (int k = fk; k <= fk + 1; k++)
			{
				v.setZ(p.z() - (float)k);
				sum += knot(i, j, k, v);
			}
		}
	}

	return sum;
}
